
package com.kweative.format;
import com.kweative.type.Node;
import com.kweative.type.Node.Type;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParser;
/**
 *
 * @author henrik
 */
public class JsonFormat extends Format {
	@Override
	public Node parse(InputStream in) {
		JsonFactory f = new JsonFactory();
		try {
			JsonParser jp = f.createJsonParser(in);
			LinkedList<Node> stack = new LinkedList<Node>();
			Node root = new Node("root");
			stack.add(root);
			Node child = null;
            String fieldName = null;
            boolean inArray = false;
            jp.nextToken();
			while(jp.hasCurrentToken()) {
				switch(jp.getCurrentToken()) {
                    case START_ARRAY:
                    case VALUE_EMBEDDED_OBJECT:
                        inArray = true;
					case START_OBJECT:
                        Type type = (inArray) ? Type.ARRAY : Type.COMPLEX;
                        if (fieldName != null) {
                            child = new Node(fieldName,type);
                        } else {
                            child = new Node(type);
                        }
						stack.getLast().addChild(child);
						stack.addLast(child);
                        fieldName = null;
						break;
                    case END_ARRAY:
                        inArray = false;
					case END_OBJECT:
						stack.removeLast();
                        fieldName = null;
						break;
					
					case FIELD_NAME:
                        fieldName = jp.getCurrentName();
						break;
					case VALUE_FALSE:
                        if (fieldName != null) {
                            stack.getLast().setAttribute(fieldName,Boolean.FALSE);
                        } else {
                            if (inArray)
                                stack.getLast().makeChild(null, Boolean.FALSE).setType(Type.VALUE);
                            else
                                stack.getLast().setValue(Boolean.FALSE);
                        }
                        fieldName = null;
						break;
					case VALUE_TRUE:
                        if (fieldName != null) {
                            stack.getLast().setAttribute(fieldName,Boolean.TRUE);
                        } else {
                            if (inArray)
                                stack.getLast().makeChild(null, Boolean.TRUE).setType(Type.VALUE);
                            else
                                stack.getLast().setValue(Boolean.TRUE);
                        }
                        fieldName = null;
						break;
					case VALUE_NULL:
                        if (fieldName != null) {
                            stack.getLast().setAttribute(fieldName,null);
                        } else {
                            if (inArray)
                                stack.getLast().makeChild(null, null);
                            else
                                stack.getLast().setValue(null);
                        }
                        fieldName = null;
						break;
					case VALUE_NUMBER_FLOAT:
                        if (fieldName != null) {
                            stack.getLast().setAttribute(fieldName,jp.getFloatValue());
                        } else {
                            if (inArray)
                                stack.getLast().makeChild(null, jp.getFloatValue()).setType(Type.VALUE);
                            else
                                stack.getLast().setValue(jp.getFloatValue());
                        }
                        fieldName = null;
						break;
					case VALUE_NUMBER_INT:
						if (fieldName != null) {
                            stack.getLast().setAttribute(fieldName,jp.getIntValue());
                        } else {
                            if (inArray)
                                stack.getLast().makeChild(null, jp.getIntValue()).setType(Type.VALUE);
                            else
                                stack.getLast().setValue(jp.getIntValue());
                        }
                        fieldName = null;
						break;
					case VALUE_STRING:
						if (fieldName != null) {
                            stack.getLast().setAttribute(fieldName,jp.getText());
                        } else {
                            if (inArray)
                                stack.getLast().makeChild(null, jp.getText()).setType(Type.VALUE);
                            else
                                stack.getLast().setValue(jp.getText());
                        }
                        fieldName = null;
						break;
				}
                jp.nextToken();
			}
			if (root.isEmpty())
				return null;
			if (root.children().size() == 1)
				return root.getLast();
			else
				return root;
		} catch (IOException ex) {
			Logger.getLogger(JsonFormat.class.getName()).log(Level.SEVERE, null, ex);
		}
		return null;
	}

	@Override
	public void serialize(Node node, OutputStream out) throws Exception {
        JsonFactory f = new JsonFactory();
        JsonGenerator jg = f.createJsonGenerator(out, JsonEncoding.UTF8);
        serializeNode(jg,node);
        jg.flush();
	}
    private void serializeNode(JsonGenerator jg,Node node) throws IOException {
        if (node.isValue()) {
            jg.writeObject(node.getValue());
            return;
        }
        if (node.isNameValue()) {
            if (node.getParent() == null)
                jg.writeObject(node.getValue());
            else if (node.getParent() == null || node.getParent().isArray()) {
                jg.writeStartObject();
                jg.writeObjectField(node.getName(), node.getValue());
                jg.writeEndObject();
            } else {
                try {
                    jg.writeObjectField(node.getName(), node.getValue());
                } catch(JsonGenerationException ex) {
                    jg.writeObject(node.getValue());
                }
            }
            return;
        }

        if (node.isSimple()) {
            jg.writeNull();
            return;
        }
        
        if (node.isComplex()) {
            jg.writeStartObject();
            if (node.getValue() != null)
                jg.writeObjectField("value", node.getValue());
            for(Iterator<String> it = node.attributes().keySet().iterator();it.hasNext();) {
                String key = it.next();
                Object value = node.getAttribute(key);
                jg.writeObjectField(key,value);
            }
            for(Node child:node.children()) {
                if (child.getName() != null)
                    jg.writeFieldName(child.getName());
                else
                    jg.writeFieldName("child");
                serializeNode(jg,child);
            }
            jg.writeEndObject();
            return;
        }
        if (node.isArray()) {
            jg.writeStartArray();
            for(Node child:node.children()) {
                serializeNode(jg,child);
            }
            jg.writeEndArray();
            return;
        }
            
    }


}
